package com.ljchen.formalProject.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ljchen.formalProject.enums.ResultCode;
import com.ljchen.formalProject.frame.exceptions.APIException;
import com.ljchen.formalProject.mapper.UserMapper;
import com.ljchen.formalProject.model.dto.UserDto;
import com.ljchen.formalProject.model.po.User;
import com.ljchen.formalProject.util.RandomImageUtil;
import kotlin.jvm.internal.Lambda;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.concurrent.TimeUnit;


@Slf4j
@Service
public class UserService extends ServiceImpl<UserMapper, User> {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private static String validateCodeKey = "::validateCode";

    @Transactional(rollbackFor = Exception.class)
    public String login(UserDto userDto) {
        String username = userDto.getUsername().trim();
        String password = userDto.getPassword().trim();
        String validateCode = userDto.getValidateCode().trim();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
        Subject subject = SecurityUtils.getSubject();
        subject.login(usernamePasswordToken);

        User user = this.getUser(username);
        this.checkValidateCode(user.getId(), validateCode);

        return "登录成功";
    }


    /**
     * 检查验证码是否正确
     *
     * @param userId       用户id
     * @param validateCode 验证代码
     */
    public void checkValidateCode(String userId, String validateCode) {
        //万能验证码
        if (validateCode.equals("1234")) {
            return;
        }
        if (StrUtil.isEmpty(validateCode)) {
            throw new APIException(ResultCode.ERROR, "验证码不能为空");
        }
        String validateCodeFromRedis = (String) redisTemplate.opsForValue().get(userId + validateCodeKey);
        if (StrUtil.isEmpty(validateCodeFromRedis)) {
            throw new APIException(ResultCode.ERROR, "验证码已过期");
        }
        if (!validateCodeFromRedis.equals(validateCode)) {
            throw new APIException(ResultCode.ERROR, "验证码校验失败");
        }
    }


    /**
     * 获取验证码
     * 这边有个不合理的地方，前端获取校验码的时候，要带上一个随机值key，然后点击登录按钮的时候同样将这个key值传递过来
     * 这个功能后续在进行修改
     *
     * @param userId 用户id
     * @return {@link String}
     */
    public String getValidateCode(String userId) {
        try {
            String code = RandomUtil.randomString(4);
            String lowerCaseCode = code.toLowerCase();
            log.info(lowerCaseCode);
            redisTemplate.opsForValue().set(userId + validateCodeKey, code, 60, TimeUnit.SECONDS);
            return RandomImageUtil.generate(code);
        } catch (IOException e) {
            throw new APIException(ResultCode.ERROR, "验证码生成失败：" + e.getMessage());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public String register(UserDto userDto) {
        String username = userDto.getUsername().trim();
        String password = userDto.getPassword().trim();
        String salt = RandomUtil.randomString(4);
        User user = this.getUser(username);
        if (ObjectUtil.isEmpty(user)) {
            throw new APIException(ResultCode.ERROR, "该用户已存在");
        }
        user = new User();
        BeanUtil.copyProperties(userDto, user);
        String encryptionPassword = new SimpleHash("MD5", password, ByteSource.Util.bytes(salt), 5).toHex();
        user.setPassword(encryptionPassword);
        user.setSalt(salt);
        this.save(user);
        return "用户注册成功";
    }

    private User getUser(String username) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, username);
        return this.getOne(wrapper);
    }
}
